// bslmt_fastpostsemaphore.h                                          -*-C++-*-

#ifndef INCLUDED_BSLMT_FASTPOSTSEMAPHORE
#define INCLUDED_BSLMT_FASTPOSTSEMAPHORE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a semaphore class optimizing 'post'.
//
//@CLASSES:
//  bslmt::FastPostSemaphore: semaphore class optimizing 'post'
//
//@SEE_ALSO: bslmt_semaphore
//
//@DESCRIPTION: This component defines a semaphore, 'bslmt::FastPostSemaphore',
// with the 'post' operation being optimized at the potential expense of other
// operations.  In particular, 'bslmt::FastPostSemaphore' is an efficient
// synchronization primitive that enables sharing of a counted number of
// resources.  'bslmt::FastPostSemaphore' supports the methods 'timedWait',
// 'enable', and 'disable' in addition to the standard semaphore methods.
//
// Commonly, during periods of time when the protected resource is scarce (the
// semaphore count is frequently zero) and threads are frequently blocked on
// wait methods, pessimizing the performance of the threads that block will
// have little effect on overall performance.  In this case, optimizing 'post'
// *may* be a performance improvement.  Note that when the resource is
// plentiful, there are no blocked threads and we expect the differences
// between semaphore implementations to be trivial.
//
///Supported Clock-Types
///---------------------
// 'bsls::SystemClockType' supplies the enumeration indicating the system clock
// on which timeouts supplied to other methods should be based.  If the clock
// type indicated at construction is 'bsls::SystemClockType::e_REALTIME', the
// 'absTime' argument passed to the 'timedWait' method should be expressed as
// an *absolute* offset since 00:00:00 UTC, January 1, 1970 (which matches the
// epoch used in 'bsls::SystemTime::now(bsls::SystemClockType::e_REALTIME)'.
// If the clock type indicated at construction is
// 'bsls::SystemClockType::e_MONOTONIC', the 'absTime' argument passed to the
// 'timedWait' method should be expressed as an *absolute* offset since the
// epoch of this clock (which matches the epoch used in
// 'bsls::SystemTime::now(bsls::SystemClockType::e_MONOTONIC)'.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: A Simple Queue
///- - - - - - - - - - - - -
// This example illustrates a very simple fixed-size queue where potential
// clients can push integers to a queue, and later retrieve the integer values
// from the queue in FIFO order.  Also, 'waitUntilEmpty' is implemented to
// depict the common usage of 'getDisabledState'.
//
// First, we define the 'IntQueue' class:
//..
//  class IntQueue {
//      // FIFO queue of integer values.
//
//      // DATA
//      bsl::vector<int>         d_data;            // queue values
//
//      bslmt::FastPostSemaphore d_pushSem;         // resource availability
//                                                  // for push
//
//      bslmt::FastPostSemaphore d_popSem;          // resource availability
//                                                  // for pop
//
//      bsls::AtomicUint         d_pushIdx;         // index to push to
//
//      bsls::AtomicUint         d_popIdx;          // index to pop from
//
//      mutable bslmt::Mutex     d_emptyMutex;      // blocking point for
//                                                  // 'waitUntilEmpty'
//
//      mutable bslmt::Condition d_emptyCondition;  // condition variable for
//                                                  // 'waitUntilEmpty'
//
//      // NOT IMPLEMENTED
//      IntQueue(const IntQueue&);
//      IntQueue& operator=(const IntQueue&);
//
//    public:
//      // PUBLIC CONSTANTS
//      enum ReturnValue {
//          e_SUCCESS  = bslmt::FastPostSemaphore::e_SUCCESS,   // indicates
//                                                              // success
//
//          e_DISABLED = bslmt::FastPostSemaphore::e_DISABLED,  // indicates
//                                                              // queue is
//                                                              // disabled
//
//          e_FAILED = bslmt::FastPostSemaphore::e_FAILED       // indicates
//                                                              // failure
//      };
//
//      // CREATORS
//      explicit
//      IntQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator = 0);
//          // Create an 'IntQueue' object with the specified 'capacity'.
//          // Optionally specify a 'basicAllocator' used to supply memory.  If
//          // 'basicAllocator' is 0, the currently installed default allocator
//          // is used.
//
//      //! ~IntQueue() = default;
//          // Destroy this object.
//
//      // MANIPULATORS
//      void disablePopFront();
//          // Disable dequeueing from this queue.  All subsequent invocations
//          // of 'popFront' and 'waitUntilEmpty' will fail immediately.  All
//          // blocked invocations of 'popFront' and 'waitUntilEmpty' will fail
//          // immediately.  If the queue is already dequeue disabled, this
//          // method has no effect.
//
//      void enablePopFront();
//          // Enable dequeuing.  If the queue is not dequeue disabled, this
//          // method has no effect.
//
//      int popFront(int *value);
//          // Remove the element from the front of this queue and load that
//          // element into the specified 'value'.  If the queue is empty,
//          // block until it is not empty.  Return 0 on success, and a nonzero
//          // value if the queue is disabled.
//
//      int pushBack(int value);
//          // Append the specified 'value' to the back of this queue, blocking
//          // until either space is available - if necessary - or the queue is
//          // disabled.  Return 0 on success, and a nonzero value if the queue
//          // is disabled.
//
//      // ACCESSORS
//      int waitUntilEmpty() const;
//          // Block until all the elements in this queue are removed.  Return
//          // 0 on success, and a non-zero value if the queue is not empty and
//          // 'isPopFrontDisabled()'.  A blocked thread waiting for the queue
//          // to empty will return a non-zero value if 'disablePopFront' is
//          // invoked.
//  };
//..
// Next, implement the queue:
//..
//  // CREATORS
//  IntQueue::IntQueue(bsl::size_t capacity, bslma::Allocator *basicAllocator)
//  : d_data(capacity, basicAllocator)
//  , d_pushSem(static_cast<int>(capacity))
//  , d_popSem(0)
//  , d_pushIdx(0)
//  , d_popIdx(0)
//  , d_emptyMutex()
//  , d_emptyCondition()
//  {
//  }
//
//  // MANIPULATORS
//  void IntQueue::disablePopFront()
//  {
//      d_popSem.disable();
//  }
//
//  void IntQueue::enablePopFront()
//  {
//      d_popSem.enable();
//  }
//
//  int IntQueue::popFront(int *value)
//  {
//      // wait for available element
//
//      int rv = d_popSem.wait();
//      if (0 != rv) {
//          return rv;                                                // RETURN
//      }
//
//      *value = d_data[d_popIdx++ % d_data.size()];
//
//      d_pushSem.post();  // signal additional empty element
//
//      if (0 == d_popSem.getValue()) {
//          {
//              bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
//          }
//          d_emptyCondition.broadcast();
//      }
//
//      return 0;
//  }
//
//  int IntQueue::pushBack(int value)
//  {
//      // wait for an empty element
//
//      int rv = d_pushSem.wait();
//      if (0 != rv) {
//          return rv;                                                // RETURN
//      }
//
//      d_data[d_pushIdx++ % d_data.size()] = value;
//
//      d_popSem.post();  // signal additional available element
//
//      return 0;
//  }
//
//  // ACCESSORS
//  int IntQueue::waitUntilEmpty() const
//  {
//      bslmt::LockGuard<bslmt::Mutex> guard(&d_emptyMutex);
//
//      int state = d_popSem.getDisabledState();
//
//      while (d_popSem.getValue()) {
//          if (state != d_popSem.getDisabledState()) {
//              return e_DISABLED;                                    // RETURN
//          }
//          d_emptyCondition.wait(&d_emptyMutex);
//      }
//
//      return e_SUCCESS;
//  }
//..
// Then, declare an instance of 'IntQueue':
//..
//  IntQueue queue(10);
//..
// Next, populate some values:
//..
//  assert(0 == queue.pushBack(5));
//  assert(0 == queue.pushBack(7));
//  assert(0 == queue.pushBack(3));
//..
// Now, pop and verify the values:
//..
//  int value;
//
//  assert(0 == queue.popFront(&value));
//  assert(5 == value);
//
//  assert(0 == queue.popFront(&value));
//  assert(7 == value);
//
//  assert(0 == queue.popFront(&value));
//  assert(3 == value);
//..
// Finally, use 'waitUntilEmpty' to verify the queue is empty:
//..
//  assert(IntQueue::e_SUCCESS == queue.waitUntilEmpty());
//..

#include <bslscm_version.h>

#include <bslmt_condition.h>
#include <bslmt_fastpostsemaphoreimpl.h>
#include <bslmt_lockguard.h>
#include <bslmt_mutex.h>
#include <bslmt_threadutil.h>

#include <bsls_atomicoperations.h>
#include <bsls_libraryfeatures.h>
#include <bsls_systemclocktype.h>
#include <bsls_timeinterval.h>
#include <bsls_types.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#include <bslmt_chronoutil.h>

#include <bsl_chrono.h>
#endif

namespace BloombergLP {
namespace bslmt {

                         // =======================
                         // class FastPostSemaphore
                         // =======================

class FastPostSemaphore {
    // This class implements a semaphore type, optimized for 'post', for thread
    // synchronization.

    // PRIVATE TYPES
    typedef FastPostSemaphoreImpl<bsls::AtomicOperations,
                                  bslmt::Mutex,
                                  bslmt::Condition,
                                  bslmt::ThreadUtil> Impl;

    // DATA
    Impl d_impl;

    // NOT IMPLEMENTED
    FastPostSemaphore(const FastPostSemaphore&);
    FastPostSemaphore& operator=(const FastPostSemaphore&);

  public:
    // PUBLIC CONSTANTS
    enum ReturnValue {
        e_SUCCESS     = Impl::e_SUCCESS,      // indicates success

        e_DISABLED    = Impl::e_DISABLED,     // indicates semaphore is
                                              // disabled

        e_TIMED_OUT   = Impl::e_TIMED_OUT,    // indicates operation timed out

        e_WOULD_BLOCK = Impl::e_WOULD_BLOCK,  // indicates operation would
                                              // block ('tryWait')

        e_FAILED      = Impl::e_FAILED        // indicates failure reported
                                              // from 'd_condition'
    };

    // CREATORS
    explicit
    FastPostSemaphore(
    bsls::SystemClockType::Enum clockType = bsls::SystemClockType::e_REALTIME);
        // Create a 'FastPostSemaphore' object initially having a count of 0.
        // Optionally specify a 'clockType' indicating the type of the system
        // clock against which the 'bsls::TimeInterval' 'absTime' timeouts
        // passed to the 'timedWait' method are to be interpreted (see
        // {Supported Clock-Types} in the component-level documentation).  If
        // 'clockType' is not specified then the realtime system clock is used.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    explicit
    FastPostSemaphore(const bsl::chrono::system_clock&);
        // Create a 'FastPostSemaphore' object initially having a count of 0.
        // Use the realtime system clock as the clock against which the
        // 'absTime' timeouts passed to the 'timedWait' methods are interpreted
        // (see {Supported Clock-Types} in the component-level documentation).

    explicit
    FastPostSemaphore(const bsl::chrono::steady_clock&);
        // Create a 'FastPostSemaphore' object initially having a count of 0.
        // Use the monotonic system clock as the clock against which the
        // 'absTime' timeouts passed to the 'timedWait' methods are interpreted
        // (see {Supported Clock-Types} in the component-level documentation).
#endif

    explicit
    FastPostSemaphore(
    int                         count,
    bsls::SystemClockType::Enum clockType = bsls::SystemClockType::e_REALTIME);
        // Create a 'FastPostSemaphore' object initially having the specified
        // 'count'.  Optionally specify a 'clockType' indicating the type of
        // the system clock against which the 'bsls::TimeInterval' 'absTime'
        // timeouts passed to the 'timedWait' method are to be interpreted (see
        // {Supported Clock-Types} in the component-level documentation).  If
        // 'clockType' is not specified then the realtime system clock is used.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    FastPostSemaphore(int count, const bsl::chrono::system_clock&);
        // Create a 'FastPostSemaphore' object initially having the specified
        // 'count'.  Use the realtime system clock as the clock against which
        // the 'absTime' timeouts passed to the 'timedWait' methods are
        // interpreted (see {Supported Clock-Types} in the component-level
        // documentation).

    FastPostSemaphore(int count, const bsl::chrono::steady_clock&);
        // Create a 'FastPostSemaphore' object initially having the specified
        // 'count'.  Use the monotonic system clock as the clock against which
        // the 'absTime' timeouts passed to the 'timedWait' methods are
        // interpreted (see {Supported Clock-Types} in the component-level
        // documentation).
#endif

    //! ~FastPostSemaphore() = default;
        // Destroy this object.

    // MANIPULATORS
    void enable();
        // Enable waiting on this semaphore.  If the semaphore is not disabled,
        // this call has no effect.

    void disable();
        // Disable waiting on this semaphore.  All subsequent invocations of
        // 'wait', 'tryWait', and 'timedWait' will fail immediately.  All
        // blocked invocations of 'wait' and 'timedWait' will fail immediately.
        // If the semaphore is already disabled, this method will have no
        // effect.

    void post();
        // Atomically increment the count of this semaphore.

    void post(int value);
        // Atomically increase the count of this semaphore by the specified
        // 'value'.  The behavior is undefined unless 'value > 0'.

    int take(int maximumToTake);
        // If the count of this semaphore is positive, reduce the count by the
        // lesser of the count and the specified 'maximumToTake' and return the
        // magnitude of the change to the count.  Otherwise, do nothing and
        // return 0.

    int takeAll();
        // If the count of this semaphore is positive, reduce the count to 0
        // and return the original value of the count.  Otherwise, do nothing
        // and return 0.

    int timedWait(const bsls::TimeInterval& absTime);
        // If this semaphore is initially disabled, or becomes disabled while
        // blocking, return 'e_DISABLED' with no effect on the count.
        // Otherwise, block until the count of this semaphore is a positive
        // value or the specified 'absTime' timeout expires.  If the count of
        // this semaphore is a positive value, return 0 and atomically
        // decrement the count.  If the 'absTime' timeout expires, return
        // 'e_TIMED_OUT' with no effect on the count.  Return 'e_FAILED' if an
        // error occurs.  Errors are unrecoverable.  After an error, the
        // semaphore may be destroyed, but any other use has undefined
        // behavior.  'absTime' is an *absolute* time represented as an
        // interval from some epoch, which is determined by the clock indicated
        // at construction (see {Supported Clock-Types} in the component
        // documentation).

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    template <class CLOCK, class DURATION>
    int timedWait(const bsl::chrono::time_point<CLOCK, DURATION>& absTime);
        // If this semaphore is initially disabled, or becomes disabled while
        // blocking, return 'e_DISABLED' with no effect on the count.
        // Otherwise, block until the count of this semaphore is a positive
        // value or the specified 'absTime' timeout expires.  If the count of
        // this semaphore is a positive value, return 0 and atomically
        // decrement the count.  If the 'absTime' timeout expires, return
        // 'e_TIMEDOUT' with no effect on the count.  Return 'e_FAILED' if an
        // error occurs.  Errors are unrecoverable.  After an error, the
        // semaphore may be destroyed, but any other use has undefined
        // behavior.  'absTime' is an *absolute* time represented as an
        // interval from some epoch, which is determined by the clock indicated
        // at construction (see {Supported Clock-Types} in the component
        // documentation).
#endif

    int tryWait();
        // If this semaphore is initially disabled, return 'e_DISABLED' with no
        // effect on the count.  Otherwise, if the count of this semaphore is a
        // positive value, return 0 and atomically decrement the count.  If
        // this semaphore is not disabled and the count of this semaphore is
        // not a positive value, return 'e_WOULD_BLOCK' with no effect on the
        // count.

    int wait();
        // If this semaphore is initially disabled, or becomes disabled while
        // blocking, return 'e_DISABLED' with no effect on the count.
        // Otherwise, block until the count of this semaphore is a positive
        // value, return 0 and atomically decrement the count.  Return
        // 'e_FAILED' if an error occurs.

    // ACCESSORS
    bsls::SystemClockType::Enum clockType() const;
        // Return the clock type used for timeouts.

    int getDisabledState() const;
        // Return an odd value if this semaphore is wait disabled, and an even
        // value otherwise.  The returned value can be used to detect a rapid
        // short sequence of 'disable' and 'enable' invocations by comparing
        // the value returned by 'getDisabledState' before and after the
        // sequence.  For example, for any initial state of a semaphore
        // instance 'obj':
        //..
        //  int state = obj.getDisabledState();
        //  obj.disable();
        //  obj.enable();
        //  ASSERT(state != obj.getDisabledState());
        //..
        // This functionality is useful in higher-level components to determine
        // if this semaphore was disabled during an operation.

    int getValue() const;
        // Return the current value ('count > 0 ? count : 0') of this
        // semaphore.

    bool isDisabled() const;
        // Return 'true' if this semaphore is wait disabled, and 'false'
        // otherwise.  Note that the semaphore is created in the "wait enabled"
        // state.
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                         // -----------------------
                         // class FastPostSemaphore
                         // -----------------------

// CREATORS
inline
FastPostSemaphore::FastPostSemaphore(bsls::SystemClockType::Enum clockType)
: d_impl(clockType)
{
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
inline
FastPostSemaphore::FastPostSemaphore(const bsl::chrono::system_clock&)
: d_impl(bsls::SystemClockType::e_REALTIME)
{
}

inline
FastPostSemaphore::FastPostSemaphore(const bsl::chrono::steady_clock&)
: d_impl(bsls::SystemClockType::e_MONOTONIC)
{
}
#endif

inline
FastPostSemaphore::FastPostSemaphore(int                         count,
                                     bsls::SystemClockType::Enum clockType)
: d_impl(count, clockType)
{
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
inline
FastPostSemaphore::FastPostSemaphore(int                              count,
                                     const bsl::chrono::system_clock&)
: d_impl(count, bsls::SystemClockType::e_REALTIME)
{
}

inline
FastPostSemaphore::FastPostSemaphore(int                              count,
                                     const bsl::chrono::steady_clock&)
: d_impl(count, bsls::SystemClockType::e_MONOTONIC)
{
}
#endif

// MANIPULATORS
inline
void FastPostSemaphore::disable()
{
    d_impl.disable();
}

inline
void FastPostSemaphore::enable()
{
    d_impl.enable();
}

inline
void FastPostSemaphore::post()
{
    d_impl.post();
}

inline
void FastPostSemaphore::post(int value)
{
    d_impl.post(value);
}

inline
int FastPostSemaphore::take(int maximumToTake)
{
    return d_impl.take(maximumToTake);
}

inline
int FastPostSemaphore::takeAll()
{
    return d_impl.takeAll();
}

inline
int FastPostSemaphore::timedWait(const bsls::TimeInterval& absTime)
{
    return d_impl.timedWait(absTime);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
template <class CLOCK, class DURATION>
inline
int FastPostSemaphore::timedWait(
                       const bsl::chrono::time_point<CLOCK, DURATION>& absTime)
{
    return bslmt::ChronoUtil::timedWait(this, absTime);
}
#endif

inline
int FastPostSemaphore::tryWait()
{
    return d_impl.tryWait();
}

inline
int FastPostSemaphore::wait()
{
    return d_impl.wait();
}

// ACCESSORS
inline
bsls::SystemClockType::Enum FastPostSemaphore::clockType() const
{
    return d_impl.clockType();
}

inline
int FastPostSemaphore::getDisabledState() const
{
    return d_impl.getDisabledState();
}

inline
int FastPostSemaphore::getValue() const
{
    return d_impl.getValue();
}

inline
bool FastPostSemaphore::isDisabled() const
{
    return d_impl.isDisabled();
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
